package lk.icta.core.web.action;

import lk.icta.common.AppConstants;
import lk.icta.common.ELandRuntimeException;
import lk.icta.common.ErrorCodes;
import lk.icta.common.dao.DSDivisionDAO;
import lk.icta.common.dao.DistrictDAO;
import lk.icta.common.dao.GNDivisionDAO;
import lk.icta.common.dao.ProvinceDAO;
import lk.icta.common.dao.impl.DSDivisionDAOImpl;
import lk.icta.common.dao.impl.DistrictDAOImpl;
import lk.icta.common.dao.impl.GNDivisionDAOImpl;
import lk.icta.common.dao.impl.ProvinceDAOImpl;
import lk.icta.common.entity.DocumentCategory;
import lk.icta.common.service.DocumentManagementService;
import lk.icta.common.service.impl.DocumentManagementServiceImpl;
import lk.icta.core.dao.*;
import lk.icta.core.dao.impl.BoundaryTypeDAOImpl;
import lk.icta.core.dao.impl.GeologicalTypeDAOImpl;
import lk.icta.core.dao.impl.LocalAuthorityDAOImpl;
import lk.icta.core.entity.*;
import lk.icta.core.service.*;
import lk.icta.core.service.impl.AlienationManagementServiceImpl;
import lk.icta.core.service.impl.LandAssociationManagementServiceImpl;
import lk.icta.core.service.impl.MortgageManagementServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.*;

public class AlienationDetailsAction extends LandDetailsAction {

    private static final Logger logger = LoggerFactory.getLogger(AlienationDetailsAction.class);

    private final MortgageManagementService mortgageManagementService;
    private final LandAssociationManagementService landAssociationManagementService;
    //private final LandRailwayManagementService landRailwayManagementService;
    private final LandManagementService landManagementService;

    private Alienation alienation;
    private Mortgage mortgage;
    private String page;
    private boolean deedTree;
    private String mortgageOrTransfer;
    private long grantId, immediateParentId, alienationId;
    private Map<Integer, String> landUsageList;
    private boolean updateDetailForm;
    private String calculateLeaseDate;
    private double maxExtent, minExtent;

    private LandAssociation landAssociation;


    private List<DocumentCategory> documentCategoriesAlienation = new ArrayList<DocumentCategory>();

    public AlienationDetailsAction(DocumentManagementService documentManagementService, LandManagementService
            landManagementService, AlienationManagementService alienationManagementService, GNDivisionDAO gnDivisionDAO,
                                   BoundaryTypeDAO boundaryTypeDAO, GeologicalTypeDAO geologicalTypeDAO,
                                   LocalAuthorityDAO localAuthorityDAO, MortgageManagementService mortgageManagementService,
                                   DistrictDAO districtDAO, DSDivisionDAO dsDivisionDAO,
                                   LandAssociationManagementService landAssociationManagementService, ProvinceDAO provinceDAO) {
        super(documentManagementService, landManagementService, alienationManagementService, gnDivisionDAO,
                boundaryTypeDAO, geologicalTypeDAO, localAuthorityDAO, districtDAO, dsDivisionDAO, provinceDAO);
        this.mortgageManagementService = mortgageManagementService;
        this.landAssociationManagementService=landAssociationManagementService;
        //this.landRailwayManagementService = landRailwayManagementService;
        this.landManagementService = landManagementService;
    }

//    public AlienationDetailsAction(AlienationManagementServiceImpl alienationManagementService, GNDivisionDAOImpl gnDivisionDAO, GeologicalTypeDAOImpl geologicalTypeDAO, BoundaryTypeDAOImpl boundaryTypeDAO, DocumentManagementServiceImpl documentManagementService, LocalAuthorityDAOImpl localAuthorityDAO, MortgageManagementServiceImpl mortgageManagementService, LandAssociationManagementServiceImpl landAssociationManagementService, DistrictDAOImpl districtDAO, DSDivisionDAOImpl dsDivisionDAO, ProvinceDAOImpl provinceDAO) {
//        super(documentManagementService, landManagementService, alienationManagementService, gnDivisionDAO,
//                boundaryTypeDAO, geologicalTypeDAO, localAuthorityDAO, districtDAO, dsDivisionDAO, provinceDAO);
//    }

    //loading alienation pages in tabs - todo: include in a seperate action class
    public String loadInitAlienationPage() {
        logger.debug("Loading alienation page {} ", editMode);
        try {
            //new entry under any AM
            //on edit mode
            if (landId != 0) {
                alienation = alienationManagementService.getActiveAlienationByLandId(landId, user);
                //this add only for  Report
                if (alienation == null) {
                    alienation = alienationManagementService.getPendingAlienationByLandId(landId, user);
                }
                //end

                //  land = landManagementService.getLandById(landId);
               // land = landRailwayManagementService.getLandByIdUKey(landId, user);
            }

            logger.debug("Alienation type --- {}", alienationType);
            //  populateInitInformation(user, land, alienation);
            if (alienationType == AppConstants.UN_ALIENATED) {     /* Un-Alienated Land */
                return "unAlienated";

            } else { /* Alienated Land */
                //  documentCategoriesAlienation = findDocumentCategoryList(ModuleType.INITIAL_APP);
                //   documentCategoriesAlienation = findDocumentCategoryList(ModuleType.INITIAL_APP);
                switch (alienationType) {
                    case AppConstants.LAND_PERMIT:     /* Land Permits */
                        landUsageList = populateLandUsageList(alienationType);
                        return "landPermit";

                    case AppConstants.LAND_GRANT:     /* Land Grants */
                        // mortgage = alienation.getMortgage();
                        return "landGrant";

                    case AppConstants.SHORT_TERM_LEASE:     /* Short Term Lease */
                        landUsageList = populateLandUsageList(alienationType);
                        return "shortTermLease";

                    case AppConstants.LONG_TERM_LEASE:     /* Long Term Lease */
                        landUsageList = populateLandUsageList(alienationType);
                        return "longTermLease";

                    case AppConstants.RELEASING_TO_DEPARTMENTS:     /* Release to Departments */
                        return "releaseToDepartments";

                    case AppConstants.VESTING_ORDER:     /* Vesting Order */
                        displayLocalAuthority(user.getDsDivisions().iterator().next().getId());
                        return "vestingOrder";

                    case AppConstants.SPECIAL_GRANT:     /* Special Grants */
                        landUsageList = populateLandUsageList(alienationType);
                        return "specialGrant";

                    default:
                        return "unAlienated";
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage());
            return "unAlienated";
        }
    }

    // Detailed land page load for add or edit - load land info
    public String loadInitialInformation() {
        try {
            logger.debug("Load Initial Land information of {} by {} to Detail forms", landId, getUser().getUsername());

            //land = landManagementService.getLandByIdUKey(landId, user);
          //  land = landRailwayManagementService.getLandByIdUKey(landId, user);
            //Message for migrated data
//            if (land.isMigratedRecord()) {
//                addActionMessage("This is a Migrated Land Record. Please make corrections before saving. Use the Comment for more information");
//            }

//            if (land.getInitialRecordId() != 0) {    // update detailed land
//                populateUploadedFiles(land.getId(), ModuleType.DETAILED_LAND);
//            } else {  //add detailed land
//                populateUploadedFiles(land.getId(), ModuleType.INITIAL_LAND);
//            }

            //todo: Store current Alienation Id in land table  and move this method to landAction
            //if (land.getAlienationState() != Land.AlienationState.ARCHIVED) {
                alienation = alienationManagementService.getActiveAlienationByLandId(land.getId(), user);

                if (alienation == null) {
                    alienationType = AppConstants.UN_ALIENATED;
                } else {
                    alienationType = alienation.getAlienationMethod().getId();
                }
                findDocumentCategoryList(ModuleType.INITIAL_LAND);
                populateBasicLists();
                //todo: is this needed. If it is required include it in accessor method
                populateLandInformation();

                populateAssociateDetail();
                //use for ORIP users
//                if (user.getRole().getId().equals(AppConstants.ORIP_USER_ROLE)) {
//                    loadDsAndGnListByLand(land);
//                }

                return INPUT;
//            } else {
//                logger.error("ARCHIVED land cannot be updated");
//                addActionError("ARCHIVED land cannot be updated");
//                return ERROR;
//            }
        } catch (Exception e) {
            e.printStackTrace();
            return ERROR;
        }
    }

    // Detailed land page load for Print Report - load land info
    public String loadInitialInformationReport() {
        try {
            logger.debug("Load Initial Land information Report of {} by {} to Detail forms", landId, getUser().getUsername());

            //land = landRailwayManagementService.getLandByIdUKey(landId, user);

            //Message for migrated data
            if (AppConstants.ADMIN.equals(land.getLifeCycleInfo().getCreatedUser().getUsername())) {
                addActionMessage("This is a Migrated Land Record. Please make corrections before saving. Use the Comment for more information");
            }

//            if (land.getInitialRecordId() != 0) {    // update detailed land
//                populateUploadedFiles(land.getId(), ModuleType.DETAILED_LAND);
//            } else {  //add detailed land
//                populateUploadedFiles(land.getId(), ModuleType.INITIAL_LAND);
//            }

            //todo: Store current Alienation Id in land table  and move this method to landAction
            //if (land.getAlienationState() != Land.AlienationState.ARCHIVED) {
                //alienation = alienationManagementService.getActiveAlienationByLandId(land.getId(), user);

                alienation = alienationManagementService.getActiveAlienationByLandId(land.getId(), user);
                if (alienation == null) {
                    alienation = alienationManagementService.getPendingAlienationByLandId(land.getId(), user);
                }


                if (alienation == null) {
                    alienationType = AppConstants.UN_ALIENATED;
                } else {
                    alienationType = alienation.getAlienationMethod().getId();
                    logger.error("lienation {}", alienation.getReferenceNo());
                    logger.error("alienation ID {}", alienation.getId());
                }
                findDocumentCategoryList(ModuleType.INITIAL_LAND);
                populateBasicLists();
                populatePrintData();

                //todo: is this needed. If it is required include it in accessor method
                populateLandInformation();

                populateAssociateDetail();
                return INPUT;
//            } else {
//                logger.error("ARCHIVED land cannot be updated");
//                addActionError("ARCHIVED land cannot be updated");
//                return ERROR;
//            }
        } catch (Exception e) {
            e.printStackTrace();
            return ERROR;
        }
    }

    public String addInitialLandInformation() {
        try {
            logger.debug("alienation type {}" + alienationType);

            // setting null for objects if required.
            setNullForListObjects();

            if (alienationType == AppConstants.UN_ALIENATED) {
                return addInitialUnalienatedLandInformation();
            } else {
                return addInitialAlienatedLandInformation();
            }

        } catch (Exception e) {
            e.printStackTrace();
            return ERROR;
        }
    }

    public String addDetailLandInformation() {
        logger.debug("add Detail Land Information ---------------- {} ", alienationType);

        // setting null for objects if required.
        setNullForListObjects();

        if (alienationType == AppConstants.UN_ALIENATED) {
            //find onld alienation id, if AM changed when editing
            alienation = alienationManagementService.getActiveAlienationByLandId(land.getId(), user);
            long alienationId = 0;
            if (alienation != null) {
                alienationId = alienation.getId();
            }

            return addUnalienatedLand(alienationId);
        } else {
            return addAlienatedLand();
        }
    }


    //todo : rewrite this method
    public String addChildrenLandInformation() {
        try {
//            logger.debug("Add detailed unalienated land information of {} by {}", land.getNameOfTheLand(),
//                    user.getUsername());

            //validate parent land
            if (!validateParentLand()) {
                loadInitialInformation();
                return INPUT;
            }

            land.setDocumentList(getUploadedfileList(ModuleType.DETAILED_LAND));
//            if (land.getInitialRecordId() > 0) { //modify detailed land info
//                landManagementService.updateDetailUnAlienatedLand(land, alienationId, landAssociation, user, language);
//                logger.debug("updated detailed land information of {} by {}", user.getUsername());
//
//            } else {//add new land
//                //pass Alienation id and archive if not zero
//                landManagementService.addDetailUnAlienatedLand(land, alienationId, landAssociation, user, language);
//                logger.debug("Added detailed land information of {} by {}", user.getUsername());
//            }

            //set land id to process next page (GEO info)
            landId = land.getId();

            populateDivisions(); //only for mobile app
            return "landList";   //todo: to be renamed

        } catch (ELandRuntimeException e) {
            switch (e.getErrorCode()) {
                case ErrorCodes.UNAUTHORIZED_ACCESS:
                    addActionError(getText("permission_denied.label"));
                    break;
                case ErrorCodes.ARCHIVED_LAND_UPDATING:
                    addActionError("Attempt to update archived land information");
                    break;
            }
            // e.printStackTrace();
            loadInitialInformation();
            logger.error("Error saving detailed un alienated land information: ");
            return ERROR;
        } catch (Exception e) {
            e.printStackTrace();
            loadInitialInformation();
            addActionError(getText("unable_to_save_land_details.label"));
            logger.error("Error saving detailed unalienated land information: " + e.getMessage());
            return ERROR;
        }

    }

    //todo : rewrite this method
    public String modifyChildrenLandInformation() {
        try {
//            logger.debug("Add detailed unalienated land information of {} by {}", land.getNameOfTheLand(),
//                    user.getUsername());

            //set land id to process next page (GEO info)
            landId = land.getId();

            //validate parent land
            if (!setParentLand()) {
                loadInitialInformation();

                return INPUT;
            }
            //delete documents
            deleteDocument();

            land.setDocumentList(getUploadedfileList(ModuleType.DETAILED_LAND));
//            if (land.getInitialRecordId() > 0) { //modify detailed land info
//                landManagementService.updateDetailUnAlienatedLand(land, alienationId, landAssociation, user, language);
//                logger.debug("updated detailed land information of {} by {}", user.getUsername());
//
//            } else {//add new land
//                //pass Alienation id and archive if not zero
//                landManagementService.addDetailUnAlienatedLand(land, alienationId, landAssociation, user, language);
//                logger.debug("Added detailed land information of {} by {}", user.getUsername());
//            }

            //set land id to process next page (GEO info)
            landId = land.getId();

            populateDivisions(); //only for mobile app
            return "landList";   //todo: to be renamed

        } catch (ELandRuntimeException e) {
            switch (e.getErrorCode()) {
                case ErrorCodes.UNAUTHORIZED_ACCESS:
                    addActionError(getText("permission_denied.label"));
                    break;
                case ErrorCodes.ARCHIVED_LAND_UPDATING:
                    addActionError("Attempt to update archived land information");
                    break;
            }
            // e.printStackTrace();
            loadInitialInformation();
            logger.error("Error saving detailed un alienated land information: ");
            return ERROR;
        } catch (Exception e) {
            e.printStackTrace();
            loadInitialInformation();
            addActionError(getText("unable_to_save_land_details.label"));
            logger.error("Error saving detailed unalienated land information: " + e.getMessage());
            return ERROR;
        }
    }

    //add/edit initial alienated land information
    public String addInitialAlienatedLandInformation() {
        logger.debug("Add initial alienated land information by {}", user.getUsername());
        if (!setParentLand()) {
            populateBasicLists();
            //todo: populate already input alienation details too
            return INPUT;
        }
        /*  if (land.getLandExtent() == null) {
           land.setLandExtent(0.0);
       } */
        try {
            //  alienation.getHolder().setChildren(resetChildList(alienation.getHolder().getChildren()));
            alienation.setAlienationMethod(new AlienationMethod(alienationType));
            setLandLogComment("Added alienated initial land ");
            land.setDocumentList(getUploadedfileList(ModuleType.INITIAL_LAND));

            switch (alienationType) {

                case AppConstants.LAND_GRANT:     // Land Grant [ADD / UPDATE]
                    if (deedTree) {
                        if (mortgageOrTransfer.equals("transfer")) {      // Deed information [ADD/ UPDATE]
                            Alienation immediateParent = alienationManagementService.getAlienationById(immediateParentId, user);
                            Alienation rootAlienation = alienationManagementService.getAlienationById(grantId, user);
                            Land immediateParentLand = immediateParent.getLand();
                            Land rootLand = rootAlienation.getLand();
                            //Double minExtent = calculateMinimumSubDivisionExtent(rootLand.getLandExtent(), rootLand.getMinimumFraction(), rootLand.getMinimumSubDivisionUnit());
                            if (!validateLandExtent(land.getLandExtent(), immediateParentLand.getLandExtent(), minExtent)) {
                                addActionError(getText("invalid_land_extent.label"));
                                populateBasicLists();
                                return INPUT;

                            } else {
                                double newLandExtent;
                                if (immediateParentLand.getLandExtent() != 0) {
                                    newLandExtent = immediateParentLand.getLandExtent() - land.getLandExtent();
                                } else {
                                    newLandExtent = 0.0;
                                }
                                immediateParentLand.setLandExtent(newLandExtent);
                                landManagementService.updateLand(immediateParentLand, user);
                                alienation.setRoot(rootAlienation);
                                alienation.setImmediateParent(immediateParent);
                               // landManagementService.addInitialAlienatedLand(land, alienation, user, language);
                            }
                            grantId = rootAlienation.getId();
                        }
                    } else {
                        //landManagementService.addInitialAlienatedLand(land, alienation, user, language);
                        grantId = alienation.getId();
                    }

                    addActionMessage(getText("land_details_saved_successfully.label"));
                    return "landGrant";

                case AppConstants.LONG_TERM_LEASE:     // Long Term Lease [ADD / UPDATE]
                    if (updateMode) {
                        if (mortgage != null) {
                            mortgageManagementService.updateMortgage(mortgage, getUser());
                            alienation.setMortgage(mortgage);
                        }
                        //landManagementService.addInitialAlienatedLand(land, alienation, user, language);

                    } else {
                        if (deedTree) {
                            if (mortgageOrTransfer.equals("transfer")) {

                                Holder holder = alienation.getHolder();
                                Mortgage mortgage = alienation.getMortgage();


                                if (immediateParentId > 0) {
                                    alienation = alienationManagementService.getAlienationById(immediateParentId, user);
                                    alienationManagementService.deleteDetailAlienation(alienationManagementService.getAlienationById(immediateParentId, user), user);

                                } else {
                                    alienation = alienationManagementService.getAlienationById(grantId, user);
                                    alienationManagementService.deleteDetailAlienation(alienationManagementService.getAlienationById(grantId, user), user);
                                }

                                //land = alienation.getLand();
                                landManagementService.archiveLand(alienation.getLand(), user);


                                land.setId(0);
                                //alienation.setLand(land);
                                holder.setId(0);
                                holder.getContacts().setId(0);
                                alienation.setHolder(holder);
                                alienation.setMortgage(mortgage);
                                alienation.setId(0);
                                alienation.getLeaseDetail().setId(0);

                                alienation.setRoot(alienationManagementService.getAlienationById(grantId, user));

                                alienation.setImmediateParent(alienationManagementService.getAlienationById(immediateParentId, user));

                                //landManagementService.addInitialAlienatedLand(land, alienation, user, language);

                            } else if (mortgageOrTransfer.equals("sublease")) {

                                Holder holder = alienation.getHolder();
                                Mortgage mortgage = alienation.getMortgage();

                                LeaseDetail subLeaseDetail = alienation.getLeaseDetail();

                                if (immediateParentId > 0) {
                                    alienation = alienationManagementService.getAlienationById(immediateParentId, user);
                                    alienationManagementService.deleteDetailAlienation(alienationManagementService.getAlienationById(immediateParentId, user), user);

                                } else {
                                    alienation = alienationManagementService.getAlienationById(grantId, user);
                                    alienationManagementService.deleteDetailAlienation(alienationManagementService.getAlienationById(grantId, user), user);
                                }
                                //land = alienation.getLand();

                                landManagementService.archiveLand(alienation.getLand(), user);


                                land.setId(0);
                                //alienation.setLand(land);
                                holder.setId(0);
                                holder.getContacts().setId(0);
                                alienation.setHolder(holder);
                                alienation.setMortgage(mortgage);
                                //alienation.getLeaseDetail().setLeasePeriod(subLeaseDetail.getLeasePeriod());
                                alienation.setId(0);
                                alienation.getLeaseDetail().setId(0);

                                alienation.setRoot(alienationManagementService.getAlienationById(grantId, user));

                                alienation.setImmediateParent(alienationManagementService.getAlienationById(immediateParentId, user));

                                //landManagementService.addInitialAlienatedLand(land, alienation, user, language);
                            } else if (mortgageOrTransfer.equals("mortgage")) {

                                alienation = alienationManagementService.getAlienationById(immediateParentId, user);

                                alienation.setMortgage(mortgage);

                                alienationManagementService.addMortgage(alienation, user);
                            }
                        } else {
                            //landManagementService.addInitialAlienatedLand(land, alienation, user, language);
                        }
                    }
                    if (alienation.getRoot() == null) {
                        grantId = alienation.getId();
                    } else {
                        grantId = alienation.getRoot().getId();
                    }
                    addActionMessage(getText("land_details_saved_successfully.label"));
                    return "longTermLease";

                default:
                    //landManagementService.addInitialAlienatedLand(land, alienation, user, language);
                    addActionMessage(getText("land_details_saved_successfully.label"));
                    return SUCCESS;

            }

        } catch (ELandRuntimeException e) {
            switch (e.getErrorCode()) {
                case ErrorCodes.UNAUTHORIZED_ACCESS:
                    addActionError(getText("permission_denied.label"));
                    break;
                case ErrorCodes.DUPLICATE_ENTRY:
                    // It is ok to set approval note null as this is te first step
                    land.setApprovalNote(null);
                    addActionError(getText("duplicate_alienation_entry.label"));
                    break;
            }
            populateBasicLists();
            return INPUT;
        } catch (Exception e) {
            logger.error(e.getMessage());
            e.printStackTrace();
            addActionError(getText("unable_to_save_land_details.label"));
            populateBasicLists();
            return INPUT;
        }
    }

    public String completeLandInformationTree() {
        logger.debug("Completing alienation history tree of {} by {}", grantId, user.getUsername());
        try {
            List<Alienation> alienationList = alienationManagementService.getAlienationHistoryByRootLandId(grantId, user);
            for (Alienation a : alienationList) {
                if (a != null && !Alienation.State.ARCHIVED.equals(a.getStatus())) {
                    landManagementService.completeInitLandInformation(a.getLand(), user);
                }
            }
            addActionMessage(getText("complete_init_info.label"));
            //  clearFields();
            populateBasicLists();
            return SUCCESS;
        } catch (ELandRuntimeException e) {
            switch (e.getErrorCode()) {
                case ErrorCodes.UNAUTHORIZED_ACCESS:
                    addActionError(getText("permission_denied.label"));
                    break;
                default:
                    addActionError(getText("error_occured.label"));
            }
            e.printStackTrace();
            logger.error(e.getMessage());
            return ERROR;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage());
            return ERROR;
        }
    }

    //Add detailed alienated land information
    private String addAlienatedLand() {
        //logger.debug("Add detailed alienated land information of {} by {}", land.getNameOfTheLand(), user.getUsername());
        //set land it to process next page (GEO info)
        landId = land.getId();

        //validate parent land
        if (!setParentLand()) {
            // landId = land.getId();
            loadInitialInformation();
            //  populateBasicLists(); //TODO: load existing alienation details  which are already input
            return INPUT;
        }
        try {
            //delete documents
            deleteDocument();

            //set The land association methods
            landAssociation.setAlienationMethods(populateAlienationMethods());

            land.setDocumentList(getUploadedfileList(ModuleType.DETAILED_LAND));

            alienation.setAlienationMethod(new AlienationMethod(alienationType));
//            if (land.getInitialRecordId() > 0) { //modify detailed land info
//                setLandLogComment("Modified alienated detailed land ");
//                landManagementService.updateDetailAlienatedLand(land, alienation, landAssociation, user, language);
//            } else {
//                setLandLogComment("Added alienated detailed land ");
//                landManagementService.addDetailAlienatedLand(land, alienation, landAssociation, user, language);
//            }

            //set land it to process next page (GEO info)
            landId = land.getId();

            logger.debug("forward land id to update geo info - land id {}", landId);

            populateDivisions(); //only for mobile app
            return "landList";  //TODO remame this to kml info

        } catch (ELandRuntimeException e) {
            switch (e.getErrorCode()) {
                case ErrorCodes.UNAUTHORIZED_ACCESS:
                    addActionError(getText("permission_denied.label"));
                    break;
                case ErrorCodes.ARCHIVED_LAND_UPDATING:
                    addActionError("Attempt to update archived land information");
                    break;
                case ErrorCodes.ARCHIVED_ALIENATION_UPDATING:
                    addActionError("Attempt to update an archived alienation");
                    break;
                default:
                    addActionError("Error saving detailed alienated land information");
                    break;
            }
            e.printStackTrace();
            loadInitialInformation();
            logger.error("Error saving detailed alienated land information: " + e.getMessage());
            return ERROR;

        } catch (Exception e) {
            loadInitialInformation();
            addActionError(getText("unable_to_save_land_details.label"));
            logger.error("Error saving detailed alienated land information: " + e.getMessage());
            e.printStackTrace();
            return ERROR;
        }
    }

    //add/update detailed unalienated land
    private String addUnalienatedLand(long alienationId) {
        try {
//            logger.debug("Add detailed unalienated land information of {} by {}", land.getNameOfTheLand(),
//                    user.getUsername());

            //set land id to process next page (GEO info)
            landId = land.getId();

            //validate parent land
            if (!setParentLand()) {
                loadInitialInformation();

                //populateBasicLists(); //TODO: load existing alienation details
                return INPUT;
            }
            //delete documents
            deleteDocument();

            //set The land association methods
            landAssociation.setAlienationMethods(populateAlienationMethods());

            land.setDocumentList(getUploadedfileList(ModuleType.DETAILED_LAND));
//            if (land.getInitialRecordId() > 0) { //modify detailed land info
//                logger.debug("Updating a Land ------------------------------- version - {}", land.getVersion());
//                setLandLogComment("Modified unalienated detailed land ");
//                landManagementService.updateDetailUnAlienatedLand(land, alienationId, landAssociation, user, language);
//                logger.debug("updated detailed land information of {} by {}", user.getUsername());
//
//            } else {//add new land
//                //pass Alienation id and archive if not zero
//                setLandLogComment("Added unalienated detailed land ");
//                landManagementService.addDetailUnAlienatedLand(land, alienationId, landAssociation, user, language);
//                logger.debug("Added detailed land information of {} by {}", user.getUsername());
//            }


            //set land id to process next page (GEO info)
            landId = land.getId();

            populateDivisions(); //only for mobile app
            return "landList";   //todo: to be renamed

        } catch (ELandRuntimeException e) {
            switch (e.getErrorCode()) {
                case ErrorCodes.UNAUTHORIZED_ACCESS:
                    addActionError(getText("permission_denied.label"));
                    break;
                case ErrorCodes.ARCHIVED_LAND_UPDATING:
                    addActionError("Attempt to update archived land information");
                    break;
                default:
                    addActionError(getText("unable_to_save_land_details.label"));
                    break;
            }
            // e.printStackTrace();
            loadInitialInformation();
            logger.error("Error saving detailed un alienated land information: ");
            return ERROR;
        } catch (Exception e) {
            e.printStackTrace();
            loadInitialInformation();
            addActionError(getText("unable_to_save_land_details.label"));
            logger.error("Error saving detailed unalienated land information: " + e.getMessage());
            return ERROR;
        }
    }


    public String addSubDividedLandInformation() {
        try {
//            logger.debug("Add sub divided land information of {} by {}", land.getNameOfTheLand(),
//                    user.getUsername());

            // setting null for objects if required.
            setNullForListObjects();

            //set land id to process next page (GEO info)
            landId = land.getId();

            //validate parent land
            if (!setParentLand()) {
                loadInitialInformation();
                return INPUT;
            }

            //check land extent
            if (!validateExtent(false)) {
                logger.error("Child Land extent error ");
                return ERROR;
            }

            //delete documents
            deleteDocument();

            land.setDocumentList(getUploadedfileList(ModuleType.DETAILED_LAND));
            logger.error("Add sub divided Land");
            //landManagementService.addSubDividedLand(land, user, language);

            //set land id to process next page (GEO info)
            landId = land.getId();

            populateDivisions(); //only for mobile app
            return "landList";   //todo: to be renamed

        } catch (ELandRuntimeException e) {
            switch (e.getErrorCode()) {
                case ErrorCodes.UNAUTHORIZED_ACCESS:
                    addActionError(getText("permission_denied.label"));
                    break;
                case ErrorCodes.ARCHIVED_LAND_UPDATING:
                    addActionError("Attempt to update archived land information");
                    break;
            }
            // e.printStackTrace();
            loadInitialInformation();
            logger.error("Error saving detailed un alienated land information: ");
            return INPUT;
        } catch (Exception e) {
            e.printStackTrace();
            loadInitialInformation();
            addActionError(getText("unable_to_save_land_details.label"));
            logger.error("Error saving detailed unalienated land information: " + e.getMessage());
            return INPUT;
        }
    }

    private boolean validateExtent(boolean isEdit) {
        try {
            logger.debug("validateExtent");
            double childLandExtent = land.getLandExtent();
            double parentLandExtent = 0;



            if (childLandExtent > parentLandExtent) {
                addActionError("Unable to save Sub divide Land Information, Land extent grater than parent Land");
                return false;
            }

            /*List<Land> childLandList = landManagementService.searchAllChildrenByParentLand(user, land.getParentLand().getId());
            if (childLandList != null && childLandList.size() > 0) {
                double allLandExtent = 0;
                for (Land l : childLandList) {

                    allLandExtent += l.getLandExtent();

                }

                if (isEdit) {
                    allLandExtent = allLandExtent - landManagementService.getLandById(land.getId()).getLandExtent();
                }

                allLandExtent = allLandExtent + childLandExtent;

                logger.debug("allLandExtent {}", allLandExtent);
                logger.debug("childLandExtent {}", childLandExtent);
                logger.debug("parentLandExtent {}", parentLandExtent);

                if (parentLandExtent < allLandExtent) {
                    addActionError("Unable to save Sub divide Land Information , Sub Lands extent grater than Parent Land");
                    return false;
                }
            }*/


        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }


    public String editSubDividedLandInformation() {
        try {
//            logger.debug("Edit sub divided land information of {} by {}", land.getNameOfTheLand(),
//                    user.getUsername());

            // setting null for objects if required.
            setNullForListObjects();

            //set land id to process next page (GEO info)
            landId = land.getId();

            //validate parent land
            if (!setParentLand()) {
                loadInitialInformation();
                return INPUT;
            }

            //check land extent
            if (!validateExtent(true)) {
                logger.error("Child Land extent error ");
                return ERROR;
            }

            //delete documents
            deleteDocument();

            land.setDocumentList(getUploadedfileList(ModuleType.DETAILED_LAND));
            logger.error("Edit sub divided Land");
            //landManagementService.editSubDividedLand(land, user, language);

            //set land id to process next page (GEO info)
            landId = land.getId();

            populateDivisions(); //only for mobile app
            return "landList";   //todo: to be renamed

        } catch (ELandRuntimeException e) {
            switch (e.getErrorCode()) {
                case ErrorCodes.UNAUTHORIZED_ACCESS:
                    addActionError(getText("permission_denied.label"));
                    break;
                case ErrorCodes.ARCHIVED_LAND_UPDATING:
                    addActionError("Attempt to update archived land information");
                    break;
            }
            // e.printStackTrace();
            loadInitialInformation();
            logger.error("Error saving detailed un alienated land information: ");
            return INPUT;
        } catch (Exception e) {
            e.printStackTrace();
            loadInitialInformation();
            addActionError(getText("unable_to_save_land_details.label"));
            logger.error("Error saving detailed unalienated land information: " + e.getMessage());
            return INPUT;
        }
    }

    public String loadDeedInformation() {
        logger.debug("Loading land deed information by {}", user.getUsername());
        try {
            alienation = alienationManagementService.getAlienationById(alienationId, user);
            //land = alienation.getLand();
            // populateInitInformation(user, land, alienation);
            //todo: populate uploaded files
            populateBasicLists();
            populateLandInformation();
            if (alienation.getAlienationMethod().getId() == AppConstants.LAND_GRANT) {
                if (alienation.getRoot() != null) {
                    grantId = alienation.getRoot().getId();
                } else {
                    grantId = alienation.getId();
                }

                Land rootLand = (alienation.getRoot() != null) ? alienation.getRoot().getLand() : alienation.getLand();
                alienation = null;

                maxExtent = 0.0;
                land.setId(0);
                page = "landGrant";
            } else if (alienation.getAlienationMethod().getId() == AppConstants.LONG_TERM_LEASE) {
                //  leaseDetail = alienation.getLeaseDetail();
                //calculate leaseDate
                landUsageList = populateLandUsageList(alienation.getAlienationMethod().getId());
                if (alienation.getIssueDate() != null) {
                    //setCalculateLeaseDate(calculateLeaseDate(alienation.getIssueDate(), alienation.getLeaseDetail().getLeasePeriod()));
                    logger.debug("Final Lease date  {}", calculateLeaseDate);
                }


                alienation.setHolder(null);
                alienation.setMortgage(null);
                if (mortgageOrTransfer.equals("transfer")) {
                    page = "transfer";
                } else if (mortgageOrTransfer.equals("sublease")) {
                    alienation.setIssueDate(null);
                    //alienation.getLeaseDetail().setLeasePeriod(0);
                    page = "sublease";
                }
            } else {
                page = "error";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return page;
    }

    private String calculateLeaseDate(Date issueDate, int leasePeriod) {
        if (leasePeriod == -1 || leasePeriod == 0) {
            leasePeriod = 0;
        }
        GregorianCalendar startDate = new GregorianCalendar((issueDate.getYear() + 1900), issueDate.getMonth(), issueDate.getDay());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        startDate.setLenient(false);
        startDate.add(Calendar.DAY_OF_YEAR, (365 * leasePeriod));
        logger.debug("Lease date {}", sdf.format(startDate.getTime()));
        return sdf.format(startDate.getTime());
    }


    private Double calculateMinimumSubDivisionExtent(Double rootExtent, Double minimumFraction, Double minimumSubDivisionUnit) {
        Double minimumExtent = 0.0;
        if (rootExtent != null && minimumFraction != null && minimumSubDivisionUnit != null) {
            if (minimumSubDivisionUnit < (rootExtent * minimumFraction)) {
                minimumExtent = (rootExtent * minimumFraction);
            } else {
                minimumExtent = minimumSubDivisionUnit;
            }
        } else if (rootExtent != null && minimumFraction != null) {
            minimumExtent = (rootExtent * minimumFraction);
        } else if (minimumSubDivisionUnit != null) {
            minimumExtent = minimumSubDivisionUnit;
        }
        return minimumExtent;
    }

    private Boolean validateLandExtent(Double currentExtent, Double parentExtent, Double minExtent) {
        if (currentExtent > parentExtent) {
            return false;
        }
        /**
         * Check whether the remaining land extent is larger than the minimum land extent.
         * OR
         * Remaining land extent is 0 (zero)
         */
        if ((parentExtent - currentExtent) != 0 && minExtent > (parentExtent - currentExtent)) {
            return false;
        }
        return true;
    }

    private void setNullForListObjects() {

        if (land != null) {
            //Local authority has not been selected by the user
//            if (land.getLocalAuthority() != null && land.getLocalAuthority().getId() == 0) {
//                land.setLocalAuthority(null);
//            }
            //Geological Type has not been selected by the user
            if (land.getGeologicalType() != null && land.getGeologicalType().getId() == 0) {
                land.setGeologicalType(null);
            }
            //North Boundary has not been selected by the user
            if (land.getNorthBoundaryType() != null && land.getNorthBoundaryType().getId() == 0) {
                land.setNorthBoundaryType(null);
            }
            //North East Boundary has not been selected by the user
            if (land.getNorthEastBoundaryType() != null && land.getNorthEastBoundaryType().getId() == 0) {
                land.setNorthEastBoundaryType(null);
            }
            //East Boundary has not been selected by the user
            if (land.getEastBoundaryType() != null && land.getEastBoundaryType().getId() == 0) {
                land.setEastBoundaryType(null);
            }
            //South East Boundary has not been selected by the user
            if (land.getSouthEastBoundaryType() != null && land.getSouthEastBoundaryType().getId() == 0) {
                land.setSouthEastBoundaryType(null);
            }
            //South Boundary has not been selected by the user
            if (land.getSouthBoundaryType() != null && land.getSouthBoundaryType().getId() == 0) {
                land.setSouthBoundaryType(null);
            }
            //South West Boundary has not been selected by the user
            if (land.getSouthWestBoundaryType() != null && land.getSouthWestBoundaryType().getId() == 0) {
                land.setSouthWestBoundaryType(null);
            }
            //West Boundary has not been selected by the user
            if (land.getWestBoundaryType() != null && land.getWestBoundaryType().getId() == 0) {
                land.setWestBoundaryType(null);
            }
            //North West Boundary has not been selected by the user
            if (land.getNorthWestBoundaryType() != null && land.getNorthWestBoundaryType().getId() == 0) {
                land.setNorthWestBoundaryType(null);
            }
        }

        if (alienation != null) {
            //Land permitted usage has not been selected by the user
            if (alienation.getPermittedUsage() != null && alienation.getPermittedUsage().getId() == 0) {
                alienation.setPermittedUsage(null);
            }
            //Land current usage has not been selected by the user
            if (alienation.getCurrentLandUsage() != null && alienation.getCurrentLandUsage().getId() == 0) {
                alienation.setCurrentLandUsage(null);
            }
        }
    }

    private void populateAssociateDetail(){
        landAssociation = landAssociationManagementService.getAlienationMethodMetadataByLand(landId, user);

            if (landAssociation != null) {

                if (landAssociation.getAlienationMethods() != null) {
                    alienationMethods = landAssociation.getAlienationMethods();

                    populateAlienationMethodIds();

                }
            }
    }

    public boolean isDisableMode() {
        //2- approve mode
        if (editMode == 2) {
            return true;
        }
        return false;
    }

    public Alienation getAlienation() {
        return alienation;
    }

    public void setAlienation(Alienation alienation) {
        this.alienation = alienation;
    }

    public String getPage() {
        return page;
    }

    public void setPage(String page) {
        this.page = page;
    }

    public Mortgage getMortgage() {
        return mortgage;
    }

    public void setMortgage(Mortgage mortgage) {
        this.mortgage = mortgage;
    }

    public boolean isDeedTree() {
        return deedTree;
    }

    public void setDeedTree(boolean deedTree) {
        this.deedTree = deedTree;
    }

    public String getMortgageOrTransfer() {
        return mortgageOrTransfer;
    }

    public void setMortgageOrTransfer(String mortgageOrTransfer) {
        this.mortgageOrTransfer = mortgageOrTransfer;
    }

    public long getGrantId() {
        return grantId;
    }

    public void setGrantId(long grantId) {
        this.grantId = grantId;
    }

    public long getImmediateParentId() {
        return immediateParentId;
    }

    public void setImmediateParentId(long immediateParentId) {
        this.immediateParentId = immediateParentId;
    }

    //TODO: remove this
    private Map<Integer, String> populateLandUsageList(int alienationMethodId) {
        Map landUsageList = new HashMap<Integer, String>();
        try {
            if (alienationMethodId != AppConstants.UN_ALIENATED) {
                Set<LandUsage> landUsage = alienationManagementService.
                        getAlienationMethodById(alienationMethodId, user).getLandUsage();
                for (LandUsage lu : landUsage) {
                    String val = Long.toString(lu.getId());
                    if (AppConstants.ENGLISH.equals(language)) {
                        val = lu.getEnUsageName();
                    } else if (AppConstants.SINHALA.equals(language)) {
                        val = lu.getSiUsageName();
                    } else if (AppConstants.TAMIL.equals(language)) {
                        val = lu.getTaUsageName();
                    }
                    landUsageList.put(lu.getId(), val);
                }
                //  logger.debug("Method :{} Usage :{}", alienation.getAlienationMethod().getId(), landUsage);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            logger.debug("Error", ex);
        }
        return landUsageList;
    }

    public Map<Integer, String> getLandUsageList() {
        return landUsageList;
    }

    public void setLandUsageList(Map<Integer, String> landUsageList) {
        this.landUsageList = landUsageList;
    }

    public boolean isUpdateDetailForm() {
        return updateDetailForm;
    }

    public void setUpdateDetailForm(boolean updateDetailForm) {
        this.updateDetailForm = updateDetailForm;
    }

    public List<DocumentCategory> getDocumentCategoriesAlienation() {
        return documentCategoriesAlienation;
    }

    public void setDocumentCategoriesAlienation(List<DocumentCategory> documentCategoriesAlienation) {
        this.documentCategoriesAlienation = documentCategoriesAlienation;
    }

    public long getAlienationId() {
        return alienationId;
    }

    public void setAlienationId(long alienationId) {
        this.alienationId = alienationId;
    }

    public String getCalculateLeaseDate() {
        return calculateLeaseDate;
    }

    public void setCalculateLeaseDate(String calculateLeaseDate) {
        this.calculateLeaseDate = calculateLeaseDate;
    }

    public double getMaxExtent() {
        return maxExtent;
    }

    public void setMaxExtent(double maxExtent) {
        this.maxExtent = maxExtent;
    }

    public double getMinExtent() {
        return minExtent;
    }

    public void setMinExtent(double minExtent) {
        this.minExtent = minExtent;
    }

    public LandAssociation getLandAssociation() {
        return landAssociation;
    }

    public void setLandAssociation(LandAssociation landAssociation) {
        this.landAssociation = landAssociation;
    }
}
